home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
Aminet 43
/
Aminet 43 (2001)(GTI - Schatztruhe)[!][Jun 2001].iso
/
Aminet
/
text
/
edit
/
BareED.lha
/
BareED
/
source
/
AsyncStartup.c
next >
Wrap
C/C++ Source or Header
|
2000-09-08
|
18KB
|
628 lines
/*
Start-up-code for BareED
Written 10 & 11/99 by Jörg van de Loo, Hövel 15, 47559 Kranenburg, FRG.
06 - 08/00 bug fixing, revising and adapting to GNU-C by Gunther Nikl.
Thanks, Gunther.
This code-code is freeware (written by J.v.d.Loo and Gunther Nikl) and
it's especially written in mind the MaxonC++ compiler V4 and the GNU-C
compiler V2.95.# (useful for C and ANSI-C and C++ modes).
CLI parser 'borrowed' from AZTEC-C package (modified).
This start-up-code set up argc and argv with the command line parameters
given through the CLI and with all selected files given through
Workbench, e.g. double click on project icon or shift select. Additional,
it ensures the present of a 68020 processor and Kickstart 2.0 (beta).
No stdin, stdout, stderr terminals will be set up since they are useless
for BareED.
The variables SysBase and ExecBase are only present within this
start-up-code; this ensures that the additional code of BareED doesn't
refer to the variables set up by the start-up-code. This is due to the
fact that my compiler would otherwise address them as 32 bit addresses
that will cause each time a reloc32 hunk entry. What I do is to refer to
_DOS_Base and _Exec_Base once, to set up DOSBase and SysBase in the
additional code of BareED, so that my compiler will them address from now
on within BareED indirect to the processor register a4.
When you compile this start-up-code ensure that no 68020 or higher
instructions are used before the processor check is executed, otherwise
on a plain 68000 processor this start-up-code will fail with a GURU.
This start-up-code checks for the amount of free stack, too. Since this
start-up-code uses very less stack (56 bytes when running and only up to
328 bytes while setting up variables) it should be enough when using a
stack size of only 4096 bytes for the load file of BareED, even under 3rd
party graphic emulation systems - which in fact do require much more
stack than the native Amiga OS 3 graphic system (up to 3Kb when BareED is
running).
One goal of this start-up-code is, that it gains at lot of stack (up to
1.1 Kb) compared to the original start-up-code that comes along with my
compiler (MaxonC++, Jörg).
Currently it needs a given stack size of 4052 bytes when started off a
CLI surround and 4046 bytes when started off the Workbench.
11/99 - modified this start-up-code to run-back when started off a CLI
window or called with RunCommand() or within a batch script of an
application.
06/00 - bug fixing and revising by Gunther Nikl.
07/00 - Can now be used for StormC; J.v.d.Loo
07/00 - Compliant to SAS-C; G. Nikl
08/00 - No forbid of task switching anymore, use of a semaphore lock, G. Nikl.
NOTE: According to Gunther, GNU-C 2.95.# allows startup.c/BareED.c to be
compiled in small (base relative) mode!
I originally used the GNU-Compiler that can be found on Geek
Gadgets II.
Thanks to Mr. Fish for this compilation.
And thank you Gunther for your annotations, although they first
brought me down.
This start-up code was bug fixed and improved by Gunther Nikl!
Rework for MaconC++ and StormC done by J.v.d.Loo .
NOTE: The handling of "__inline" in GNU-C / SAS-C does not complain
with legal rules! Thus "__inline" has been removed for Storm
and Maxon C/C++ compilers!
*/
#include <exec/execbase.h>
#include <exec/memory.h>
#include <dos/dosextens.h>
#include <dos/dostags.h>
#include <workbench/startup.h>
#include <clib/exec_protos.h>
#include <clib/dos_protos.h>
#if !defined(__MAXON__) && !defined(__STORM__)
#include <proto/exec.h>
#include <proto/dos.h>
#else
#include <pragma/exec_lib.h>
#include <pragma/dos_lib.h>
#endif
#ifdef __GNUC__
#define ASM
#define REG(reg,arg) arg __asm(#reg)
#else
#if !defined (__MAXON__)
#define ASM __asm
#endif
#define REG(reg,arg) register __##reg arg
#endif
#ifdef __MAXON__
extern "C" void GetBaseReg( void);
#define __saveds
#define ASM
#endif
#if defined(__MAXON__) || defined(__STORM__)
#define __inline
#define __stdargs
#ifdef __cplusplus // Tells if MAXON or STORM compilers run in C++ mode
#define main main__UjPPUc // C++ name for main
extern "ASM" main__UjPPUc( unsigned long argc, unsigned char **argv); // Real function name and parameters
#pragma - // Turn off C++ compile mode (use ANSI-C instead)
#endif
#endif
struct Message *WBenchMsg;
struct ExecBase *_Exec_Base;
struct DosLibrary *_DOS_Base;
static struct ExecBase *SysBase; // Allow only to appear within startup.c
static struct DosLibrary *DOSBase; // Allow only to appear within startup.c
static BPTR _prg_dir = -1; // Initial value, no directory remembered
static unsigned int _argc, _arg_len;
static unsigned char **_argv, *_arg_lin;
static struct Process *MasterTask = 0;
static struct SignalSemaphore _AsyncSem;
static short _Kick1;
#define DOSName (UBYTE *)"dos.library"
#define WrongCPU (UBYTE *)"ERROR: CPU < 68020!\n"
#define NotEnoughtMem (UBYTE *)"ERROR: Not enought memory!\n"
#define NotEnoughtStack (UBYTE *)"ERROR: Stack < 4096 bytes!\n"
#define WrongLIB (UBYTE *)"ERROR: Library versions < 36!\n"
#define WrongError (UBYTE *)"ERROR: Non-describt fault!\n" // ????
#define Console "CON:0/0/320/80/ERROR REPORT/AUTO/CLOSE/WAIT"
static void _main_jmp( unsigned long parlen, unsigned char *parameter);
int __stdargs main( unsigned long argc, unsigned char **argv);
// ################################################################
/* Gunther has enhanced the start-up code so that it's now possible (when
started off the CLI) that this routine is twice called: 1st time when
initialising; 2nd time, when used as main entry for detached (new created)
task! */
int ASM __saveds INIT_0_run_me_at_first_place( REG(d0,unsigned long parlen), REG(a0,unsigned char *parameter) )
{
#ifdef __MAXON__ // Since MaxonC/C++ does not understand __saveds
GetBaseReg();
#endif
_Exec_Base = SysBase = *((struct ExecBase **) 4); // From memory location 4 to exec library
_main_jmp( parlen, parameter); // We only return here if the detach from the CLI was a success!
// We jump to _main_jmp() because if we would do here
// the following stuff, d0 and a0 would ever be pre-
// reserved, which blows up the object file un-necessary
return 0; // Return code (for CLI), in case detach was a success!
}
// ################################################################
/* Following code is to avoid reloc32 entries - since they would otherwise
(when linked with the object files) called via "jsr" and not via "bsr" */
static __inline unsigned int strlenNR( register const unsigned char *str)
{
register unsigned int i = 0;
while (*str++)
i++;
return i;
}
static __inline void strncpyNR( register unsigned char *d, register const unsigned char *s, register unsigned int i)
{
while (i)
{
*d++ = *s++;
i--;
}
*d = 0;
}
static __inline void strcpyNR( register unsigned char *d, register const unsigned char *s)
{
while (*s)
*d++ = *s++;
*d = 0;
}
static __inline void strcatNR( register unsigned char *d, register const unsigned char *s)
{
while (*d)
d++;
while( *s)
*d++ = *s++;
*d = 0;
}
static __inline void strncatNR( register unsigned char *d, register const unsigned char *s, register unsigned int i)
{
while (*d)
d++;
while( i)
{
*d++ = *s++;
i--;
}
*d = 0;
}
// ################################################################
/* Function required by GNU-C linker, not necessary anymore - since
all work already done by this start-up-code! */
#ifdef __GNUC__
void __main(void)
{
}
#endif
// ################################################################
/* Taken without permission from the AZTEC-package, which in fact
can be found in books and magazines, too */
static void _cli_parse(struct Process *pp, unsigned long alen, register unsigned char *aptr)
{
register unsigned char *cp;
register struct CommandLineInterface *cli;
register unsigned char c;
cli = (struct CommandLineInterface *) BADDR( pp->pr_CLI );
cp = (unsigned char *) BADDR( cli->cli_CommandName );
_arg_len = (unsigned char) cp[0] + alen + 2;
if ( (_arg_lin = (unsigned char *) AllocMem( ((_arg_len + 7) & -8), MEMF_CLEAR ) ) == 0)
return;
c = cp[0];
strncpyNR( _arg_lin, cp + 1, c);
_arg_lin[c] = ' ';
_arg_lin[c + 1] = 0;
strncatNR( _arg_lin, aptr, alen);
_arg_lin[c] = 0;
for (_argc = 1, aptr = cp = _arg_lin + c + 1; ; _argc++)
{
while ( (c = *cp) == ' ' || c == '\t' || c == '\f' || c == '\r' || c == '\n')
cp++;
if (*cp < ' ')
break;
if (*cp == '"')
{
cp++;
while ( (c = *cp++) )
{
*aptr++ = c;
if (c == '"')
{
if (*cp == '"')
{
cp++;
}
else
{
aptr[-1] = 0;
break;
}
}
}
}
else
{
while ( (c = *cp++) && c != ' ' && c != '\t' && c != '\f' && c != '\r' && c != '\n')
*aptr++ = c;
*aptr++ = 0;
}
if (c == 0)
--cp;
}
*aptr = 0;
if ( (_argv = (unsigned char **) AllocMem( (((_argc + 1) * 4 + 7) & -8 ), MEMF_CLEAR ) ) == 0 )
{
_argc = 0;
return;
}
for (c=0, cp=_arg_lin; c < _argc; c++)
{
_argv[c] = cp;
cp += strlenNR( cp) + 1;
}
_argv[c] = 0;
}
// ################################################################
/* Allows several Workbench passed in arguments for BareED */
static void _wb_parse( struct WBStartup *msg)
{
int numargs, len, i;
char str[256], *curr; // Normally I'm against arrays on stack, but since we're
// at front of a load file it doesn't matter (because there
// is enough free stack available) and we can here avoid
// memory fragmentation through AllocMem()
numargs = msg -> sm_NumArgs;
_arg_len = (numargs + 2) * 4; // Number of arguments into amount bytes (plus 2 long words)
/* Get length in bytes of all arguments including zero bytes and drawer terminators */
i = 0;
while (i < numargs)
{
if (msg -> sm_ArgList[i] . wa_Lock)
NameFromLock( msg -> sm_ArgList[i] . wa_Lock, &str[0], 255);
_arg_len += strlenNR( &str[0]);
_arg_len += 2; // For zero byte and perhaps for drawer terminator "/" !
_arg_len += strlenNR( msg -> sm_ArgList[i] . wa_Name);
i++;
}
/* Allocate needed space for strings */
_arg_lin = (unsigned char *) AllocMem( ((_arg_len + 7) & -8), MEMF_CLEAR );
if ( !_arg_lin)
return;
_argc = numargs;
_argv = (unsigned char **) _arg_lin;
curr = _arg_lin + ((numargs + 2) * 4);
/* Create and copy drawer and filenames into allocated memory; behind the argument pointers! */
i = 0;
while (i < numargs)
{
_argv[i] = curr;
if (msg -> sm_ArgList[i] . wa_Lock)
{
NameFromLock( msg -> sm_ArgList[i] . wa_Lock, &str[0], 255);
strcpyNR( curr, &str[0]);
len = strlenNR( curr);
if ( curr[ len - 1] != ':')
{
curr[len] = '/';
len ++;
}
}
else
{
len = 0;
}
strcpyNR( curr + len, msg -> sm_ArgList[i] . wa_Name);
len = strlenNR( curr);
curr += len + 1; // Behind zero byte
i++; // Next arg
}
_prg_dir = CurrentDir( msg -> sm_ArgList[0] . wa_Lock); // Set up "progdir:"
}
// ################################################################
/* Print error code down to a console window, if there isn't one yet,
open one and give the message. */
static __inline void GiveFault( int error)
{
if (_DOS_Base) // DOSBase set up?
{
static char console[] = Console;
unsigned char *errorTxt = 0;
BPTR newStdOut;
if (_Kick1) // OS 2.0 ?
console[27] = 0; // No, OS 1.x, so remove AUTO/CLOSE/WAIT from console description
newStdOut = Open( WBenchMsg ? console : "*", MODE_NEWFILE); // Figure out if we have to open a console window
if (newStdOut) // on our own - in case we've been fired up by Workbench
{
if (error == 105)
errorTxt = WrongCPU;
if (error == 122)
errorTxt = WrongLIB;
if (error == 217)
errorTxt = NotEnoughtStack;
if (error == 103)
errorTxt = NotEnoughtMem;
if (errorTxt == 0)
errorTxt = WrongError; // ????
Write( newStdOut, errorTxt, strlenNR( errorTxt) );
if (WBenchMsg && _Kick1) // If WB-start and OS 1.x
Delay( 5*60); // Wait a while
Close( newStdOut); // Close console
}
}
}
// ################################################################
/* Code to get program's return address and to call it */
static const unsigned short _finally_code[] =
{
0x2400, // move.l D0,D2 Save error code
0x93C9, // suba.l A1,A1
0x2C78,0x0004, // movea.l (4).w,A6
0x4EAE,0xFEDA, // jsr _LVOFindTask(A6) Own task
0x2040, // movea.l D0,A0
0x2068,0x00B0, // movea.l pr_ReturnAddr(A0),A0 Get system's exit address for us
0x4FE8,0xFFFC, // lea -4(A0),sp Restore initial stack
0x2002, // move.l D2,D0 Error code to D0
0x4E75 // rts Back to system
};
// ################################################################
/* Since my compiler doesn't allow to overload a function (stub),
I use a different name: exitNR ! */
void __saveds exitNR( int error)
{
void (* ASM _finally)( REG(d0,int err) ) = (void (*ASM) ( REG( d0,int)) ) _finally_code;
#ifdef __MAXON__
GetBaseReg();
#endif
if (error)
GiveFault( error);
((struct Process *) FindTask( NULL)) -> pr_Result2 = error;
if (WBenchMsg)
{
if (_arg_lin)
{
FreeMem( _arg_lin, ((_arg_len + 7) & -8) );
if (_prg_dir != -1) // If it's the same as initial, ignore
CurrentDir( _prg_dir); // Else, lock of basic directory
}
Forbid();
ReplyMsg( WBenchMsg);
}
else
{
if (_arg_lin)
{
FreeMem(_argv, (((_argc + 1) * 4 + 7) & -8) );
FreeMem(_arg_lin, ((_arg_len + 7) & -8) );
}
}
if (_DOS_Base)
CloseLibrary( (struct Library *) _DOS_Base);
_finally( error);
}
// ################################################################
/* This function does not return to the caller ! */
static void CallMainPRG( void)
{
exitNR( main( _argc, _argv));
}
// ################################################################
/* Set up necessary things to provide an auto-detach (RunBack mode).
This is a little tricky one, which cares about the priority and
stack size of the original process, which is the setting for the
new created.
Enhanced by G. Nikl (06/00)
*/
static APTR LaunchStartup( void)
{
struct CommandLineInterface *cli;
struct Process *pr;
unsigned char *cp;
struct TagItem tag[5];
char buf[256];
pr = (struct Process *) FindTask( NULL);
cli = (struct CommandLineInterface *) BADDR( pr->pr_CLI);
cp = (unsigned char *) BADDR( cli->cli_CommandName);
// Duplicate name: slave task's name equal to master's
strncpyNR( buf, cp + 1, cp[0]);
// Settings for task to create
tag[0].ti_Tag = NP_Seglist;
tag[0].ti_Data = (ULONG) cli->cli_Module;
tag[1].ti_Tag = NP_FreeSeglist;
tag[1].ti_Data = TRUE;
tag[2].ti_Tag = NP_Name;
tag[2].ti_Data = (ULONG) buf;
tag[3].ti_Tag = NP_StackSize;
tag[3].ti_Data = *(ULONG *) pr->pr_ReturnAddr;
tag[4].ti_Tag = TAG_DONE;
// 08/00 G. Nikl
InitSemaphore(&_AsyncSem);
// 08/00 G. Nikl
ObtainSemaphore(&_AsyncSem);
// Create slave task which becomes master task
if ( (MasterTask = CreateNewProc( tag)) ) // Process created ?
{
cli->cli_Module = 0;
}
// 08/00 G. Nikl
ReleaseSemaphore(&_AsyncSem);
// Return whether success or not...
return MasterTask;
}
// ################################################################
/* This function can be twice called in case we've started from the
CLI:
First, when we fired up regularly through the user (MasterTask == 0);
when we have called the second time (MasterTask != 0), we are the
through LauchStartup() created task!
Enhanced by G. Nikl (08/00)
*/
static void _main_jmp( unsigned long parlen, unsigned char *parameter)
{
if ( !MasterTask) // First time fired up?
{
struct Process *proc = (struct Process *) FindTask( NULL);
char *lower;
if ( !proc -> pr_CLI)
{
WaitPort( &proc -> pr_MsgPort);
WBenchMsg = GetMsg( &proc -> pr_MsgPort);
}
else
{
WBenchMsg = 0;
}
// Lower Kickstart 2.0 (beta)?
_Kick1 = _Exec_Base -> LibNode . lib_Version < 36 ? TRUE : FALSE;
_DOS_Base = DOSBase = (struct DosLibrary *) OpenLibrary( DOSName, 33);
if ( !_DOS_Base || _Kick1)
exitNR( 122);
if ( !(_Exec_Base -> AttnFlags & AFF_68020)) // At least a 68020 processor?
exitNR( 105);
// Do we have at least 4000 bytes of stack available? - NOTE: the variable proc is on
// the top of the stack so using this address is the current upper bound of the stack!
lower = proc->pr_Task.tc_SPLower;
if ( (proc->pr_Task.tc_Node.ln_Type == NT_PROCESS) && proc->pr_CLI)
lower = (char *) proc->pr_ReturnAddr + sizeof( ULONG) - *(ULONG *) proc->pr_ReturnAddr;
if ( ((char *) &proc - lower) < 4000)
exitNR( 217);
if (WBenchMsg)
{
_wb_parse( (struct WBStartup *) WBenchMsg);
if ( !_arg_lin)
exitNR( 103); // Fail: no mem
}
else
{
_cli_parse( proc, parlen, parameter);
if ( !_arg_lin)
exitNR( 103);
}
// Try detach from CLI, if it fails, go on normal
if (WBenchMsg || !LaunchStartup())
{
CallMainPRG();
}
}
else // ...we're the task that detached off CLI!
{
// Wait for parent
ObtainSemaphore(&_AsyncSem);
// Release lock
ReleaseSemaphore(&_AsyncSem);
// Main loop
CallMainPRG();
}
}
// ################################################################